package cn.jbooter.restweb.autoconfigure.exception.handler;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.shiro.ShiroException;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;

import cn.jbooter.coms.constants.IBaseConstants;
import cn.jbooter.coms.exceptions.AccessException;
import cn.jbooter.coms.exceptions.BaseException;
import cn.jbooter.coms.exceptions.BusinessException;
import cn.jbooter.coms.exceptions.FrameworkException;
import cn.jbooter.coms.exceptions.LoginException;

/**
 * web层的全局异常处理器
 * 
 * @author hejian
 *
 */
public class GlobalExceptionHandlerExceptionResolver extends ExceptionHandlerExceptionResolver {
	private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandlerExceptionResolver.class);

	// 默认错误视图
	private String defaultErrorView;
	// 异常映射
	private Properties exceptionMappings;

	public String getDefaultErrorView() {
		return defaultErrorView;
	}

	public void setDefaultErrorView(String defaultErrorView) {
		this.defaultErrorView = defaultErrorView;
	}

	public Properties getExceptionMappings() {
		return exceptionMappings;
	}

	public void setExceptionMappings(Properties exceptionMappings) {
		this.exceptionMappings = exceptionMappings;
	}

	/**
	 * 打印和记录异常信息
	 * 
	 * @param exception
	 */
	public void printException(Exception exception) {
		StackTraceElement stackTraceElement = exception.getStackTrace()[0];
		String className = stackTraceElement.getClassName();
		String methodName = stackTraceElement.getMethodName();
		String fileName = stackTraceElement.getFileName();
		int lineNumber = stackTraceElement.getLineNumber();
		logger.error("\n文件:{};行号:{};方法:{};发生异常:{}", fileName, lineNumber, className + "." + methodName,
				exception.toString());
		logger.error("", exception); // 打印异常
	}

	/**
	 * 统一异常处理
	 */
	@Override
	protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request, HttpServletResponse response,
			HandlerMethod handlerMethod, Exception exception) {
		// 打印异常信息
		printException(exception);
		logger.error("=========== exception.printStackTrace() ===========");
		exception.printStackTrace();

		if (handlerMethod == null) {
			return null;
		}
		Method method = handlerMethod.getMethod();
		Class<?> beanType = handlerMethod.getBeanType();
		if (method == null) {
			return null;
		}

		ModelAndView mv = super.doResolveHandlerMethodException(request, response, handlerMethod, exception);
		ResponseBody responseBodyAnnotation = AnnotationUtils.findAnnotation(method, ResponseBody.class);
		RestController restControllerAnnotation = AnnotationUtils.findAnnotation(beanType, RestController.class);
		// 请求类型:传统同步http请求还是ajax异步请求
		String reqType = request.getHeader("X-Requested-With");
		if ("XMLHttpRequest".equalsIgnoreCase(reqType) || responseBodyAnnotation != null
				|| restControllerAnnotation != null) {
			ModelAndView modelAndView = new ModelAndView(new MappingJackson2JsonView());

			String msg = "";
			String code = "";
			boolean success = false;
			if (exception instanceof BusinessException) {
				// 业务异常
				BusinessException businessEx = (BusinessException) exception;
				msg = businessEx.getMessage();
				code = businessEx.getCode();
				//response.setStatus(520);

			} else if (exception instanceof LoginException) {
				// 登录异常
				LoginException loginEx = (LoginException) exception;
				msg = loginEx.getMessage();
				code = loginEx.getCode();
				//response.setStatus(510);

			}else if (exception instanceof AccessException) {
				// 数据访问异常
				AccessException accessEx = (AccessException) exception;
				msg = accessEx.getMessage();
				code = accessEx.getCode();
				//response.setStatus(500);

			}else if (exception instanceof FrameworkException) {
				// 框架异常
				FrameworkException frameworkEx = (FrameworkException) exception;
				msg = frameworkEx.getMessage();
				code = frameworkEx.getCode();
				//response.setStatus(500);

			}else if(exception instanceof BaseException) {
				// 自定义异常基类
				BaseException baseEx = (BaseException) exception;
				msg = baseEx.getMessage();
				code = baseEx.getCode();
				
			} 
			// shiro权限类异常
			else if (exception instanceof UnauthorizedException) {
				// 权限异常
				msg = exception.getMessage();
				msg = msg.replace("Subject does not have permission", "没有访问\\: ") + " 权限许可(505).";
				code = IBaseConstants.AUTHZ_FAIL_CODE;
				//response.setStatus(500);

			}else if (exception instanceof UnauthenticatedException) {
				// 认证异常
				msg = exception.getMessage();
				msg = "请先登录!";
				code = IBaseConstants.AUTHC_FAIL_CODE;
				//response.setStatus(500);

			}else if (exception instanceof ShiroException) {
				// shiro异常
				msg = exception.getMessage();
				code = "9999";
				//response.setStatus(500);

			} 
			// 其它异常
			else {
				msg = exception.getMessage();
				code = "9999";
				//response.setStatus(500);
				
			}
			
			logger.error("<<<===异常码:" + code + "异常信息:" + msg);
			// 返回json
			modelAndView.getModel().put("success", success);
			modelAndView.getModel().put("msg", msg);
			modelAndView.getModel().put("code", code);
			return modelAndView;
		}

		// http页面请求的异常处理
		if (mv == null) {
			Map<String, Object> params = new HashMap<String, Object>();
			params.put("success", false);
			params.put("msg", "系统异常!");
			try {
				response.reset();
			} catch (Exception e) {
				e.printStackTrace();
				logger.error(e.getMessage());
			}
			;
			mv = new ModelAndView(defaultErrorView, params);
		}

		return mv;
	}
}
